package com.ruoyi.web.controller.websocket;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.li.work.mapper.SysUserMapper;
import com.ruoyi.li.work.service.ISysUserService;
import com.ruoyi.teaching.domain.WebSocketMessage;
import com.ruoyi.teaching.domain.WebSocketTemporaryMessage;
import com.ruoyi.teaching.domain.WebSocketUserDialogue;
import com.ruoyi.teaching.mapper.WebSocketMessageMapper;
import com.ruoyi.teaching.mapper.WebSocketTemporaryMessageMapper;
import com.ruoyi.teaching.mapper.WebSocketUserDialogueMapper;
import com.ruoyi.web.controller.tool.ReflectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Slf4j
@Component
@ServerEndpoint("/webSocket/{name}")
public class WebSocket {
    //与某个客户端连接对话，通过此对客户端发送消息
    private Session session;

    private String onlineName;



    //存放所有连接的客户端
    private static final Map<String, WebSocket> webSocketConcurrentHashMap = new ConcurrentHashMap<>();

    // 离线单聊消息存储
    private static final Map<String, List<Message>> messageMap = new ConcurrentHashMap<>();

    // 离线群发消息存储
    private static final Map<String, List<Message>> groupMessageMap = new ConcurrentHashMap<>();

    // 离线临时消息存储
    private static final Map<String, List<Message>> tempMessageMap = new ConcurrentHashMap<>();
    // 离线系统消息存储
    private static final Map<String, List<Message>> systemMessageMap = new ConcurrentHashMap<>();
    // 离线好友申请消息存储
    private static final Map<String, List<Map<String, String>>> applyMessageMap = new ConcurrentHashMap<>();
    WebSocketUserDialogueMapper webSocketUserDialogueMapper = ReflectUtil.getBean(WebSocketUserDialogueMapper.class);
    WebSocketTemporaryMessageMapper webSocketTemporaryMessageMapper = ReflectUtil.getBean(WebSocketTemporaryMessageMapper.class);
    WebSocketMessageMapper webSocketMessageMapper = ReflectUtil.getBean(WebSocketMessageMapper.class);
    RedisCache redisCache = ReflectUtil.getBean(RedisCache.class);


    @OnOpen
    public void onOpen(Session session, @PathParam(value = "name") String name) {
        //默认客户端，没有重名
        this.session = session;
        this.onlineName = name;
        webSocketConcurrentHashMap.put(name, this);
        // 检查用户是否有离线消息
        if (!CollectionUtils.isEmpty(messageMap.get(name))) {
            synchronized (messageMap.get(name)) {
                if (messageMap.containsKey(name)) {
                    List<Message> list = messageMap.get(name);
                    if (!CollectionUtils.isEmpty(list)) {
                        for (Message mes : list) {
                            appointSending(mes.getSender(), name, mes.getMessage(), mes.getType(), mes.getFileName(), mes.getDialogueId(), mes.getTime());
                        }
                    }
                    // 发送完毕后，移除该用户的离线消息
                    messageMap.remove(name);
                    log.info("单聊离线消息发送完毕，移除消息" + name);
                }
            }
        }

        if (!CollectionUtils.isEmpty(groupMessageMap.get(name))) {
            synchronized (groupMessageMap.get(name)) {
                if (groupMessageMap.containsKey(name)) {
                    List<Message> list = groupMessageMap.get(name);
                    if (!CollectionUtils.isEmpty(list)) {
                        for (Message mes : list) {
                            try {
                                webSocketConcurrentHashMap.get(name).session.getBasicRemote().sendText(mes.getDialogueId() + "&" + mes.getUser() + "&" + mes.getName() + "&" + mes.getMessage() + "&" + mes.getType() + "&" + mes.getFileName() + "&" + mes.getSender() + "&" + mes.getTime());
                            } catch (IOException e) {
                                log.info("群聊离线异常", e.toString());
                            }
                        }
                    }
                    // 发送完毕后，移除该用户的离线消息
                    groupMessageMap.remove(name);
                    log.info("群聊离线消息发送完毕，移除消息" + name);
                }
            }
        }
        if (!CollectionUtils.isEmpty(systemMessageMap.get(name))) {
            synchronized (systemMessageMap.get(name)) {
                if (systemMessageMap.containsKey(name)) {
                    List<Message> list = systemMessageMap.get(name);
                    if (!CollectionUtils.isEmpty(list)) {
                        for (Message mes : list) {
                            try {
                                webSocketConcurrentHashMap.get(name).session.getBasicRemote().sendText("system:"+name + "&" + mes.getMessage() + "&" +mes.getDialogueId());
                            } catch (IOException e) {
                                log.info("公告离线异常", e.toString());
                            }
                        }
                    }
                    // 发送完毕后，移除该用户的离线消息
                    systemMessageMap.remove(name);
                    log.info("公告离线消息发送完毕，移除消息" + name);
                }
            }
        }

        if (!CollectionUtils.isEmpty(tempMessageMap.get(name))) {
            synchronized (tempMessageMap.get(name)) {
                if (tempMessageMap.containsKey(name)) {
                    List<Message> list = tempMessageMap.get(name);
                    if (!CollectionUtils.isEmpty(list)) {
                        for (Message mes : list) {
                            try {
                                webSocketConcurrentHashMap.get(name).session.getBasicRemote().sendText("3" + ":" + mes.getSender() + "&" + mes.getMessage() + "&" + mes.getType() + "&" + mes.getFileName() + "&" + mes.getDialogueId() + "&" + mes.getTime());

                            } catch (IOException e) {
                                log.info("临时离线异常", e.toString());
                            }
                        }
                    }
                    // 发送完毕后，移除该用户的离线消息
                    tempMessageMap.remove(name);
                    log.info("临时离线消息发送完毕，移除消息" + name);
                }
            }
        }

        if (!CollectionUtils.isEmpty(applyMessageMap.get(name))) {
            synchronized (applyMessageMap.get(name)) {
                if (applyMessageMap.containsKey(name)) {
                    List<Map<String, String>> list = applyMessageMap.get(name);
                    if (!CollectionUtils.isEmpty(list)) {
                        for (Map<String, String> mes : list) {
                            try {
                                        webSocketConcurrentHashMap.get(name).session.getBasicRemote().sendText(mes.get("SendName") + "&" + mes.get("ReceiveName")  + "&" +mes.get("type"));

                            } catch (IOException e) {
                                log.info("临时离线异常", e.toString());
                            }
                        }
                    }
                    // 发送完毕后，移除该用户的离线消息
                    applyMessageMap.remove(name);
                    log.info("好友申请离线消息发送完毕，移除消息" + name);
                }
            }
        }
        log.info("【webSocket连接成功】当前连接人数为：" + webSocketConcurrentHashMap.size() + "，此人为：" + name);
    }

    @OnClose
    public void onClose() {
        webSocketConcurrentHashMap.computeIfPresent(onlineName, (key, webSocket) -> {
            log.info("【webSocket{}退出成功】当前连接人数为：" + (webSocketConcurrentHashMap.size() - 1), onlineName);
            return null; // `return null` 相当于 `remove()`
        });
        log.info("当前连接人数为：" + (webSocketConcurrentHashMap.size()));
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        log.info("websocket异常");
        throwable.printStackTrace();
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        log.info("接收到的消息为：" + message);
        Map map = JSON.parseObject(message, Map.class);
        String isAdd = (String)map.get("type");
        if ("add".equals(isAdd)){
            String receiveName = map.get("ReceiveName").toString();
            String sendName = map.get("SendName").toString();
//           好友通知
            try {
                if (webSocketConcurrentHashMap.containsKey(receiveName)){
                    webSocketConcurrentHashMap.get(receiveName).session.getBasicRemote().sendText(sendName + "&" + receiveName + "&" +map.get("type"));
                    log.info("好友通知发送成功");
                }else {
                    synchronized (applyMessageMap) {
                        applyMessageMap.computeIfAbsent(receiveName, k -> Collections.synchronizedList(new ArrayList<Map<String,String>>()));
                        synchronized (applyMessageMap.get(receiveName)) {
                            Map<String, String> tempMap =  new HashMap<>();
                            tempMap.put("ReceiveName", receiveName);
                            tempMap.put("SendName", sendName);
                            tempMap.put("type", map.get("type").toString());
                            applyMessageMap.get(receiveName).add(tempMap);
                        }
                    }
                }
            } catch (IOException e) {
                log.info("添加好友推送异常", e.toString());
            }
        }else {
            Message mes = JSON.parseObject(message, Message.class);
            //此处可以指定发送，或者群发，或者xxxx的
            if (!mes.isTempMessage()) {
                log.info("非临时消息");
                if (null != mes.getName() && !mes.getName().equals("")) {
                    log.info("消息接收者名称" + mes.getName());
                    String[] users = mes.getName().split(",");

                    if (users.length == 1) {
                        log.info("单聊");
                        String name = users[0];
                        if (!webSocketConcurrentHashMap.containsKey(name)) {
                            log.info("当前登录人" + onlineName);
                            //                    synchronized (messageMap) {
                            messageMap.computeIfAbsent(name, k -> Collections.synchronizedList(new ArrayList<Message>()));
                            //                    }
                            synchronized (messageMap.get(name)) {
                                mes.setSender(onlineName);
                                messageMap.get(name).add(mes);
                            }

                        } else {
                            log.info("单聊" + name + "用户在线，直接发送");
                            appointSending(onlineName, name, mes.getMessage(), mes.getType(), mes.getFileName(), mes.getDialogueId(), mes.getTime());
                        }
                    } else {//群发
                        log.info("群聊");
                        groupSending(users, mes.getMessage(), session, mes.getName(), mes.getDialogueId(), mes.getUser(), mes.getType(), mes.getFileName(), mes.getTime());
                    }
                } else {//公告
                    log.info("公告");
                    if (null != mes.getFileName() && !mes.getFileName().equals("")) {
                        groupSendingAll(mes.getMessage(), session);
                    } else {
                        groupSendingAll(mes.getMessage(), session);
                    }
                }
            } else {
                log.info("临时消息");
                String name = mes.getName();//接收者名称
                if (StringUtils.hasText(mes.getName())) {
                    ISysUserService iSysUserService = ReflectUtil.getBean(ISysUserService.class);
                    SysUser sysUser = iSysUserService.selectUserByUserName(onlineName);
                    SysUser sysUser1 = iSysUserService.selectUserByUserName(name);
                    WebSocketTemporaryMessage tempMessage = new WebSocketTemporaryMessage();
                    tempMessage.setMessage(mes.getMessage());
                    tempMessage.setUserB(sysUser.getUserId());
//                报存临时聊天记录
                    QueryWrapper<WebSocketTemporaryMessage> webSocketTemporaryMessageQueryWrapper = new QueryWrapper<>();
                    webSocketTemporaryMessageQueryWrapper.eq("user_a", sysUser.getUserId());
                    webSocketTemporaryMessageQueryWrapper.eq("user_b", sysUser1.getUserId());
                    Integer aLong = Math.toIntExact(webSocketTemporaryMessageMapper.selectCount(webSocketTemporaryMessageQueryWrapper));

                    WebSocketTemporaryMessage webSocketTemporaryMessage = new WebSocketTemporaryMessage();
                    webSocketTemporaryMessage.setUserA(sysUser.getUserId());
                    webSocketTemporaryMessage.setUserAAvatar(sysUser.getAvatar());
                    webSocketTemporaryMessage.setUserANickName(sysUser.getNickName());
                    webSocketTemporaryMessage.setUserB(sysUser1.getUserId());
                    webSocketTemporaryMessage.setUserBAvatar(sysUser1.getAvatar());
                    webSocketTemporaryMessage.setUserBNickName(sysUser1.getNickName());
                    webSocketTemporaryMessage.setMessage(mes.getMessage());
                    List<WebSocketUserDialogue> webSocketUserDialogues = webSocketUserDialogueMapper.selectUserDialogueBy(sysUser.getUserId(), sysUser1.getUserId(),3);
                    log.info("查询对话框" + webSocketUserDialogues);
                    Integer dialogueId = null;
                    if (CollectionUtils.isEmpty(webSocketUserDialogues)) {
                        WebSocketUserDialogue webSocketUserDialogue = new WebSocketUserDialogue();
                        webSocketUserDialogue.setAUserId(sysUser.getUserId());
                        webSocketUserDialogue.setAUserName(sysUser.getUserName());
                        webSocketUserDialogue.setAUserRemarks(sysUser.getNickName());
                        webSocketUserDialogue.setBUserId(sysUser1.getUserId());
                        webSocketUserDialogue.setBUserName(sysUser1.getUserName());
                        webSocketUserDialogue.setBUserRemarks(sysUser1.getNickName());
                        webSocketUserDialogue.setType(3);
                        webSocketUserDialogue.setATopUp(0);
                        webSocketUserDialogue.setBTopUp(0);
                        webSocketUserDialogue.setCreateTime(new Date());
                        Integer integer = webSocketUserDialogueMapper.insertUserDialogue(webSocketUserDialogue);//对话框id
                        dialogueId = webSocketUserDialogue.getId();
                    } else {
                        dialogueId = webSocketUserDialogues.get(0).getId();
                    }
                    webSocketTemporaryMessage.setDialogueId(dialogueId);
                    Integer insert = webSocketTemporaryMessageMapper.insert(webSocketTemporaryMessage);
                    if (insert == 0) {
                        return;
                    }
                    log.info("对话框数量:" + webSocketUserDialogues.size() + "当前对话框id:" + dialogueId);
                    if (aLong >= 5) {
                        try {
                            webSocketConcurrentHashMap.get(onlineName).session.getBasicRemote().sendText(name + "&" + "mesLimit" + "&" + dialogueId + "&" + mes.getTime());
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                        return;
                    }
                    if (null != webSocketConcurrentHashMap.get(mes.getName())) {
                        try {
                            webSocketConcurrentHashMap.get(name).session.getBasicRemote().sendText("3" + ":" + onlineName + "&" + mes.getMessage() + "&" + mes.getType() + "&" + mes.getFileName() + "&" + dialogueId + "&" + mes.getTime());
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }else {
//                    临时消息不在线
                        log.info("当前登录人" + onlineName);
                        //                    synchronized (messageMap) {
                        tempMessageMap.computeIfAbsent(name, k -> Collections.synchronizedList(new ArrayList<Message>()));
                        //                    }
                        synchronized (tempMessageMap.get(name)) {
                            mes.setSender(onlineName);
                            mes.setDialogueId(dialogueId);
                            tempMessageMap.get(name).add(mes);
                        }
                    }
                }
            }
        }


    }

    //群发
    public void groupSending(String[] users, String message, Session exIncludeSession, String names, Integer dialogueId, String user, String type, String fileName, String time) {
        try {
            for (String name : users) {
                if (webSocketConcurrentHashMap.containsKey(name)) {
                    log.info(name + "在线");
                    if (null != webSocketConcurrentHashMap.get(name)) {
                        if (exIncludeSession == webSocketConcurrentHashMap.get(name).session) {
                            continue;
                        }
                        log.info("群聊" + name + "用户在线，直接发送");
                        webSocketConcurrentHashMap.get(name).session.getBasicRemote().sendText(dialogueId + "&" + user + "&" + names + "&" + message + "&" + type + "&" + fileName + "&" + onlineName + "&" + time);
                    }
                } else {
                    log.info(name + "不在线");
                    Message mes = new Message();
                    if (!onlineName.equals(name)) {
//                        synchronized (groupMessageMap) {
                        groupMessageMap.computeIfAbsent(name, k -> Collections.synchronizedList(new ArrayList<Message>()));
//                        }
                        synchronized (groupMessageMap.get(name)) {
                            mes.setDialogueId(dialogueId);
                            mes.setUser(user);
                            mes.setName(name);
                            mes.setMessage(message);
                            mes.setType(type);
                            mes.setFileName(fileName);
                            mes.setTime(time);
                            mes.setSender(onlineName);
                            groupMessageMap.get(name).add(mes);
                        }

                    }
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //公告
    public void groupSendingAll(String message, Session exIncludeSession) {

        for (String name : webSocketConcurrentHashMap.keySet()) {
            try {
                if (null != webSocketConcurrentHashMap.get(name)) {
                    if (exIncludeSession == webSocketConcurrentHashMap.get(name).session)
                        continue;
                    System.out.println(name);
                    WebSocketMessage webSocketMessage = new WebSocketMessage();
                    webSocketMessage.setMessage(message);
                    webSocketMessage.setDialogueId(0);
                    webSocketMessage.setCreateTime(new Date());
                    webSocketMessageMapper.insertSystemNotice(webSocketMessage);
                    Integer mesId = webSocketMessage.getId();
                    webSocketConcurrentHashMap.get(name).session.getBasicRemote().sendText("system:"+name + "&" + message + "&" +mesId);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
//        公告离线消息暂存
        SysUserMapper sysUserMapper = ReflectUtil.getBean(SysUserMapper.class);
        List<SysUser> userList = sysUserMapper.selectUserAll();
        Set<String> userNameList = userList.stream().map(SysUser::getUserName).collect(Collectors.toSet());
        Set<String> onlineNameList = new HashSet<>(webSocketConcurrentHashMap.keySet());

//        List<String> offlineNameList = userNameList.stream().filter(name -> !onlineNameList.contains(name)).collect(Collectors.toList());
        List<String> offlineNameList = redisCache.getCacheList("offlineNameList");
                if (CollectionUtils.isEmpty(offlineNameList)) {
            offlineNameList = userNameList.stream().filter(name -> !onlineNameList.contains(name)).collect(Collectors.toList());
            long l = redisCache.setCacheList("offlineNameList", offlineNameList);
            log.info("緩存111111:" + l);
        }
        for (String name : offlineNameList) {
            Message mes = new Message();
            WebSocketMessage webSocketMessage = new WebSocketMessage();
            webSocketMessage.setMessage(message);
            webSocketMessage.setDialogueId(0);
            webSocketMessage.setCreateTime(new Date());
            webSocketMessageMapper.insertSystemNotice(webSocketMessage);
            systemMessageMap.computeIfAbsent(name, k -> Collections.synchronizedList(new ArrayList<Message>()));
            synchronized (systemMessageMap.get(name)) {
                mes.setDialogueId(webSocketMessage.getId());
                mes.setMessage(message);
                systemMessageMap.get(name).add(mes);
            }
        }

    }

    //指定发
    public void appointSending(String sender, String name, String message, String type, String fileName, Integer dialogueId, String time) {
        System.out.println(123456);
        try {
            if (null != webSocketConcurrentHashMap.get(name)) {
                webSocketConcurrentHashMap.get(name).session.getBasicRemote().sendText("0" + ":" + sender + "&" + message + "&" + type + "&" + fileName + "&" + dialogueId + "&" + time);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Session getSession() {
        return session;
    }
}
